﻿#pragma once

#include <learn/check.h>
#include <learn/geom/vec.h>
#include <map>
#include <memory>
#include <vector>

namespace xi
{

namespace painter
{

using namespace xi::geom;

template <typename T> class Buffer
{
  private:
    struct _Impl
    {
        std::vector<T> data;

        int width{};
        int height{};

        _Impl() = default;
        _Impl(int width, int height) : data(width * height, T{}), width(width), height(height)
        {
        }
        _Impl(_Impl &&) = delete;
    };

    std::shared_ptr<_Impl> m_impl;

  public:
    /**
     * @brief 空构造
     *
     */
    Buffer() : m_impl(std::make_shared<_Impl>())
    {
    }

    /**
     * @brief 给出尺寸
     *
     * @param[in] width
     * @param[in] height
     */
    Buffer(int width, int height) : m_impl(std::make_shared<_Impl>(width, height))
    {
    }

    _Impl *operator->() const noexcept
    {
        return m_impl.get();
    }

    T get(int x, int y) const
    {
        ASSERT_INFO(x >= 0 && x < m_impl->width && y >= 0 && y < m_impl->height, "pixel out of range");
        return m_impl->data[x + y * m_impl->width];
    }

    void set(int x, int y, T t)
    {
        ASSERT_INFO(x >= 0 && x < m_impl->width && y >= 0 && y < m_impl->height, "pixel out of range");
        m_impl->data[x + y * m_impl->width] = t;
    }

    void resize(int width, int height)
    {
        m_impl->data.resize(width * height);
        m_impl->width = width;
        m_impl->height = height;
    }

    void refresh(T t)
    {
        std::fill(m_impl->data.begin(), m_impl->data.end(), t);
    }
};

struct VAO
{
  private:
    struct _Impl
    {
        // 顶点属性名称、数据类型、数据指针
        std::map<std::string, std::tuple<int, void*>> layout;

        _Impl() = default;
        _Impl(_Impl &&) = delete;
    };

    std::shared_ptr<_Impl> m_impl;

  public:
    VAO() : m_impl(std::make_shared<_Impl>())
    {
    }

    _Impl *operator->() const noexcept
    {
        return m_impl.get();
    }
};

struct VBO
{
  private:
    struct _Impl
    {
        std::vector<char> buffer;

        _Impl() = default;
        _Impl(_Impl &&) = delete;
    };

    std::shared_ptr<_Impl> m_impl;

  public:
    VBO() : m_impl(std::make_shared<_Impl>())
    {
    }

    _Impl *operator->() const noexcept
    {
        return m_impl.get();
    }
};

struct EBO
{
  private:
    struct _Impl
    {
        std::vector<char> buffer;

        _Impl() = default;
        _Impl(_Impl &&) = delete;
    };

    std::shared_ptr<_Impl> m_impl;

  public:
    EBO() : m_impl(std::make_shared<_Impl>())
    {
    }

    _Impl *operator->() const noexcept
    {
        return m_impl.get();
    }
};

} // namespace painter

} // namespace xi
